//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-2003  Microsoft Corporation

Module Name: dispatcher.cpp

Abstract:


Notes:


--*/
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//

#include "dispatcher.h"
#include "chanel.h"

#include <pegdser.h>
#include <excpt.h>
#include "rhaapi.h"

#include <args.h>
#include <ioctl_cfg.h>

// Interface to RHA.DLL
RHA_FUNCTION_LIST g_MUX_RHAFunctionList;
HINSTANCE   g_MUX_hRHAInst = NULL;


/* The best baud rate for RIL MUX mode */
#define RIL_MUX_BEST_BAUDRATE   CBR_57600

#ifdef GSM0710_UNITARY_TESTS
    extern HANDLE  hUTest_FakeRxEv; 
    extern HANDLE  hUTest_FakeTxEv; 
    extern HANDLE  hFakeCOMPort;

    extern CRITICAL_SECTION csFileAccess;
    #define UTest_SEND_ENTER()    \
                    DWORD dwWaitTime;                                    \
                    dwBytesNb = 0;                                        \
                    EnterCriticalSection(&csFileAccess)    ;                \
                    SetFilePointer(m_hComPort, 0, NULL, FILE_BEGIN);        \
                    SetEndOfFile(m_hComPort);

    #define UTest_SEND_LEAVE()    \
                LeaveCriticalSection(&csFileAccess);                \
                SetEvent(hUTest_FakeTxEv);                            \
                dwWaitTime = (dwBytesNb * 9) * 1000 / 115200;        \
                if (dwWaitTime > 2)                                    \
                    Sleep(dwWaitTime)

    #define READFILE(P1, P2, P3, P4, P5) \
                EnterCriticalSection(&csFileAccess);    \
                ReadFile(P1, P2, P3, P4, P5);            \
                LeaveCriticalSection(&csFileAccess)
#else
    #define UTest_SEND_ENTER()
    #define UTest_SEND_LEAVE()

    #define READFILE(P1, P2, P3, P4, P5) \
                ReadFile(P1, P2, P3, P4, P5)     
#endif    // GSM0710_UNITARY_TESTS

//=============================================================
// WinCE exception management
//=============================================================
DWORD DispFilterException(DWORD dwExceptionCode, wchar_t *pMsg)
{
    // send log message
    SENDERR((TEXT("710MUX: Dispatcher EXCEPTION : 0x%x in %s\r\n"), dwExceptionCode, pMsg));

    return EXCEPTION_CONTINUE_SEARCH;
}

inline BOOL ValidControlField(BYTE c) 
{
    c |= SET_PF_MASK;
    return (BOOL) ( CTRL_FIELD_UIH == c ||
                    CTRL_FIELD_UA == c ||
                    CTRL_FIELD_SABM == c ||                    
                    CTRL_FIELD_DM == c ||  
                    CTRL_FIELD_DISC == c );
}

//=============================================================

unsigned char g_TmpucCrcTbl[256] = 
{   
    //reversed, 8-bit, poly=0x07
    0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75,
    0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,

    0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69,
    0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,

    0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D,
    0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,

    0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51,
    0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,

    0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05,
    0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,

    0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19,
    0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,

    0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D,
    0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,

    0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21,
    0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,

    0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95,
    0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,

    0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89,
    0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,

    0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD,
    0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,

    0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1,
    0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,

    0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5,
    0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,

    0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9,
    0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,

    0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD,
    0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,

    0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1,
    0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
};

BYTE TmpCalcFCS(BYTE* pBuf, DWORD cbBuf)
{
    BYTE bFCS = 0xFF;
    
    while (cbBuf--)
    {
        bFCS = g_TmpucCrcTbl[bFCS ^ *pBuf++];        
    }

    bFCS = ~bFCS & 0xFF;

    return bFCS;
}

//--------------------------
// constructor / destructor
//--------------------------
CDispatcher::CDispatcher()
{
    m_bIsOk = false;
    m_bIsInitialized = false;

    // reset all internal data
    ResetInternalData();
}

CDispatcher::~CDispatcher()
{
    TerminateAll();
}

void CDispatcher::ResetInternalData()
{
    m_bIsOk = false;
    m_bResetRequired = false;
    m_lPowerState = OPERATIONAL;
    m_bTerminateAll = m_bTerminate = false;

    if ( !m_bIsInitialized )
    {
        m_hLaunchTimeOutEv    = NULL;
        m_hCancelTimeOutEv    = NULL;
        m_hTimeOutTh        = NULL;

        m_hComPort = INVALID_HANDLE_VALUE;

        m_bFrameFlag = GSM0710_BASIC_FLAG; //basic option is default
        m_cbMaxFrameData = UIH_MAX_DATA;
    }

    m_dwTimeOut = GSM0710_T3;
    m_dwTimeOutBeforeReset = 0;

    m_hSenseComTh = NULL;
    m_hWakeUpTh     = NULL;

    m_hGoAheadEv        = NULL;
    m_hWokenUpEv        = NULL;
    m_hLaunchWakeUpEv    = NULL;
    m_hRebootOKEv        = NULL;
    m_hRebootKOEv        = NULL;
    m_hCancelResetEv    = NULL;

    for (int i = 0; i < MAX_CHANEL_ID; i++)
    {
        m_ppChanel[i] = NULL;
    }

    m_lConnectedChanelNb = 0;
    m_pCtrlChanel = NULL;
}

void SendExitMuxModeFrame(HANDLE hComPort)
{
    DWORD len = 0;
    BYTE aucData[8] = {0xF9, 0x03, CTRL_FIELD_UIH, 0x1 | (2<<1), 0xC3, 0x1, 0x0, 0xF9};

    BYTE bFCS = TmpCalcFCS(aucData + 1, 5);
    aucData[6] = bFCS;

    WriteFile(hComPort, (LPCVOID)aucData, sizeof(aucData), &len, NULL);      
}

BOOL CDispatcher::ConfigureModemBaudRate(DWORD dwTargetBaudRate)
{
    DCB        dcb;
    DWORD  adwBaudRate[3] = {57600, 115200, 38400};
    char sTmpBuf[AT_CMD_SIZE];
    char *pCurChar = NULL;
    ULONG    len = 0;
    DWORD dwMask = 0;
    int iNbTimeOut = 0, iNbChar = 0;
    bool bOkReceived = FALSE;

    // Set com port
    if (!GetCommState(m_hComPort, &dcb))
    {
        SENDERR((TEXT("710MUX: Dispatcher: -> ConfigureModemBaudRate Failed in GetCommState\r\n")));
        return FALSE;
    }

    for (DWORD dwLoopI =0; dwLoopI < 3; dwLoopI++)
    {
        dcb.BaudRate = adwBaudRate[dwLoopI];

        // Set com port
        if (!SetCommState(m_hComPort, &dcb))
        {
            SENDERR((TEXT("710MUX: Dispatcher: -> ConfigureModemBaudRate Failed in SetCommState (baudrate = %d)\r\n"), adwBaudRate[dwLoopI]));
            return FALSE;
        }

        // Modem Init for Siemens MC
        sprintf(sTmpBuf, "AT\r");
        WriteFile(m_hComPort, sTmpBuf, strlen(sTmpBuf), &len, NULL);

        // wait for TE's answer with time-out
        m_dwTimeOut = GSM0710_T3;
        iNbTimeOut = 3;
        iNbChar = AT_CMD_SIZE - 1;
        bOkReceived = false;
        dwMask = 0;

        pCurChar = sTmpBuf;
        memset(sTmpBuf, 0, AT_CMD_SIZE);
        while(iNbTimeOut && iNbChar && !bOkReceived )
        {
             SetEvent(m_hLaunchTimeOutEv);
             if (WaitCommEvent(m_hComPort, &dwMask, NULL) && (EV_RXCHAR & dwMask))
             {
                 // cancel timeout
                 PulseEvent(m_hCancelTimeOutEv);
     
                 // get response and throw it away (if we've been able to read => it works)
                 if ( ReadFile(m_hComPort, pCurChar, iNbChar, &len, NULL) && len >= 0)
                 {
                     pCurChar += len;
                     iNbChar -= len;
                     if (NULL != strstr(sTmpBuf, OK_RSP))
                     {
                         bOkReceived = true;
                     }
                 }
             }
            else
            {
                // time-out on read
                DWORD dwErrors;

                if (EV_ERR & dwMask)
                {
                    ClearCommError(m_hComPort, &dwErrors, NULL);
                }

                if (EV_BREAK & dwMask)
                {
                    ClearCommBreak(m_hComPort);
                }

                iNbTimeOut--;
                SENDERR((TEXT("710MUX: Dispatcher: -> ConfigureModemBaudRate Time out\r\n")));
            }
        }

        if (bOkReceived)
        {
            sprintf(sTmpBuf, "AT+IPR=%u\r", dwTargetBaudRate);
            WriteFile(m_hComPort, sTmpBuf, strlen(sTmpBuf), &len, NULL);

            // wait for TE's answer with time-out
            m_dwTimeOut = GSM0710_T3;
            iNbTimeOut = 3;
            iNbChar = AT_CMD_SIZE - 1;
            bOkReceived = false;
            dwMask = 0;

            pCurChar = sTmpBuf;
            memset(sTmpBuf, 0, AT_CMD_SIZE);
            while(iNbTimeOut && iNbChar && !bOkReceived )
            {
                SetEvent(m_hLaunchTimeOutEv);
                if (WaitCommEvent(m_hComPort, &dwMask, NULL) && (EV_RXCHAR & dwMask))
                {
                    // cancel timeout
                    PulseEvent(m_hCancelTimeOutEv);

                    // get response and throw it away (if we've been able to read => it works)
                    if ( ReadFile(m_hComPort, pCurChar, iNbChar, &len, NULL) && len >= 0)
                    {
                        pCurChar += len;
                        iNbChar -= len;
                        if (NULL != strstr(sTmpBuf, OK_RSP))
                        {
                            bOkReceived = true;
                        }
                    }
                }
                else
                {
                    // time-out on read
                    DWORD dwErrors;

                    if (EV_ERR & dwMask)
                    {
                        ClearCommError(m_hComPort, &dwErrors, NULL);
                    }

                    if (EV_BREAK & dwMask)
                    {
                        ClearCommBreak(m_hComPort);
                    }

                    iNbTimeOut--;
                }
            }

            if (!bOkReceived )
            {
                SENDERR((TEXT("710MUX: Dispatcher: -> ConfigureModemBaudRate Failed in rsp on AT+IPR\r\n")));
                return FALSE;
            }

            SENDERR((TEXT("710MUX: Dispatcher: -> Init, AT+IPR (dwTargetBaudRate = %d)Successful\r\n"), dwTargetBaudRate));
            return TRUE;
        }

        SENDERR((TEXT("710MUX: Dispatcher: -> ConfigureModemBaudRate Try next baudrate \r\n")));
    }
    
    return FALSE;
}

//--------------------------------
// Init
//    - Dispatcher initialization
//--------------------------------
bool CDispatcher::Init()
{    
    HKEY hKey = NULL;
    LONG lStatus;

    #define COM_NAME_SIZE 50
    TCHAR szComName[COM_NAME_SIZE];

    #define REBOOT_EV_MAX_MAX 64
    TCHAR szRebootOKEventName[REBOOT_EV_MAX_MAX];
    TCHAR szRebootKOEventName[REBOOT_EV_MAX_MAX];

    DWORD dwSize = 0; 
    DWORD dwValType;
    DWORD dwBaudRate = 0;

    BSP_ARGS args;
    DWORD size;

    if(!KernelIoControl(IOCTL_GET_BSP_ARGS,NULL,0,&args,sizeof(args),&size))
    {
        RETAILMSG(1, (_T("RIL Driver: IOCTL_GET_BSP_ARGS failed!") ));
        return FALSE;
    }

    if((args.TechBoardUsage != BSP_ARGS_TECHBOARD_BASEBAND))
    {
        SENDLOG((TEXT("710MUX: Baseband is disabled in bootload.\r\n")));
        return FALSE;
    }
    SENDLOG((TEXT("710MUX: Baseband is enabled in bootload.\r\n")));
	
    SENDLOG((TEXT("710MUX: CDispatcher - Init\r\n")));
    
    // get settings from registry
    //-------------------------------
    lStatus = RegOpenKeyEx(    HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\GSM07_10"),
                            0, 0, &hKey);
    if ( ERROR_SUCCESS  != lStatus )
    {
        goto error;
    }

    dwSize = COM_NAME_SIZE * sizeof(TCHAR);
    dwValType = REG_SZ;
    lStatus = RegQueryValueEx( hKey, TEXT("ComPort"), NULL, &dwValType, 
                               (unsigned char *) szComName, &dwSize); 
    if ( ERROR_SUCCESS  != lStatus )
    {
        RegCloseKey(hKey);
        goto error;
    }

    // Didn't load BaudRate Item from Register files
    dwSize = sizeof(DWORD);
    dwValType = REG_DWORD;
    //RegQueryValueEx( hKey, TEXT("BaudRate"), NULL, &dwValType, (LPBYTE) &dwBaudRate, &dwSize); 

    if (!m_bIsInitialized)
    {
        // Frame option 
        //  -if the value doesn't exist or 0 => basic option
        //  -if the value is other than 0 => advanced option
        //DWORD dwVal = 0;
        //dwSize = sizeof(DWORD);
        //dwValType = REG_DWORD;
        //RegQueryValueEx( hKey, TEXT("FrameOption"), NULL, &dwValType, (LPBYTE) &dwVal, &dwSize);
        //m_bFrameFlag = (dwVal ? GSM0710_ADVANCED_FLAG : GSM0710_BASIC_FLAG);


        //Frame size. Default is 64 bytes, range: 1 - 0x7FFF
        //dwVal = 0;
        //dwSize = sizeof(DWORD);
        //dwValType = REG_DWORD;

        //RegQueryValueEx( hKey, TEXT("FrameSize"), NULL, &dwValType, (LPBYTE) &dwVal, &dwSize);

        //if (dwVal > 1 && dwVal < 0x8000)
        //{
        //    m_cbMaxFrameData = dwVal;
        //}

        /* Basic Mux Mode and Max frame data bytes fixed in 98 bytes */
        m_bFrameFlag = GSM0710_BASIC_FLAG;
        m_cbMaxFrameData = UIH_MAX_DATA;
    }

    RegCloseKey(hKey);

    // get Reboot Event Name
    lStatus = RegOpenKeyEx(    HKEY_LOCAL_MACHINE, TEXT("Drivers\\BuiltIn\\RIL"), 
                            0, 0, &hKey);
    if ( ERROR_SUCCESS  == lStatus )
    {

        dwSize = REBOOT_EV_MAX_MAX * sizeof(TCHAR);
        dwValType = REG_SZ;
        lStatus = RegQueryValueEx( hKey, TEXT("RebootOKEvName"), NULL, &dwValType, 
                                   (unsigned char *) szRebootOKEventName, &dwSize); 
        if ( ERROR_SUCCESS  != lStatus )
        {
            // no event name
            szRebootOKEventName[0] = 0;
        }

        lStatus = RegQueryValueEx( hKey, TEXT("RebootKOEvName"), NULL, &dwValType, 
                                   (unsigned char *) szRebootKOEventName, &dwSize); 
        if ( ERROR_SUCCESS  != lStatus )
        {
            // no event name
            szRebootKOEventName[0] = 0;
        }

        RegCloseKey(hKey);
    }
    else
    {
        // no event name
        szRebootOKEventName[0] = 0;
        szRebootKOEventName[0] = 0;
    }

    // create threads and synchro mechanisms
    //----------------------------------------
    m_bTerminate = false;
    m_bTerminateAll = false;
    InitializeCriticalSection(&m_csSend);

    // create events
    if (NULL == m_hLaunchTimeOutEv)
    {
        m_hLaunchTimeOutEv = CreateEvent(0,FALSE,FALSE,NULL);
    }
    if (NULL == m_hCancelTimeOutEv)
    {
        m_hCancelTimeOutEv = CreateEvent(0,FALSE,FALSE,NULL); 
    }
    m_hGoAheadEv = CreateEvent(0,TRUE,FALSE,NULL);        // manual reset event
    m_hWokenUpEv = CreateEvent(0,FALSE,FALSE,NULL);
    m_hLaunchWakeUpEv    = CreateEvent(0,FALSE,FALSE,NULL);

    // create cancel reset event
    m_hCancelResetEv = CreateEvent(NULL, FALSE, FALSE, NULL);

    if(0 != szRebootOKEventName[0])
    {
        m_hRebootOKEv = CreateEvent(0,TRUE,FALSE,szRebootOKEventName);    // manual reset event
    }
    else
    {
        m_hRebootOKEv = NULL;
    }

    if(0 != szRebootKOEventName[0])
    {
        m_hRebootKOEv = CreateEvent(0,TRUE,FALSE,szRebootKOEventName);    // manual reset event
    }
    else
    {
        m_hRebootKOEv = NULL;
    }

    if (NULL == m_hLaunchTimeOutEv || NULL == m_hCancelTimeOutEv || 
        NULL == m_hGoAheadEv || NULL == m_hWokenUpEv || NULL == m_hLaunchWakeUpEv ||
        NULL == m_hCancelResetEv)
    {
        goto error;
    }

    // create threads
    m_hSenseComTh = CreateThread(NULL,0, SenseComTh, (LPVOID)this, 0,NULL);
    m_hWakeUpTh = CreateThread(NULL,0, WakeUpTh, (LPVOID)this, 0,NULL);

    if (NULL == m_hTimeOutTh)
    {
        m_hTimeOutTh = CreateThread(NULL,0, TimeOutTh, (LPVOID)this, 0,NULL);
    }

    if (NULL == m_hSenseComTh || NULL == m_hTimeOutTh || NULL == m_hWakeUpTh)
    {
        goto error;
    }


    // open underlying COM port and init it
    //--------------------------------------
    SENDLOG((TEXT("710MUX: CDispatcher - Start Init open underlying COM port\r\n")));

    // Configure 
    g_MUX_RHAFunctionList.pfnRHA_Init = NULL;
    g_MUX_RHAFunctionList.pfnRHA_DeInit = NULL;
    g_MUX_RHAFunctionList.pfnRHA_RadioPwrOn = NULL;
    g_MUX_RHAFunctionList.pfnRHA_RadioPwrOff = NULL;
    g_MUX_RHAFunctionList.pfnRHA_IsRadioPwrOn = NULL;
    g_MUX_RHAFunctionList.pfnRHA_ActivateVoicePath = NULL;
    g_MUX_RHAFunctionList.pfnRHA_DeactivateVoicePath = NULL;
    g_MUX_RHAFunctionList.pfnRHA_AdjustTxVolume = NULL;
    g_MUX_RHAFunctionList.pfnRHA_AdjustRxVolume = NULL;
    g_MUX_RHAFunctionList.pfnRHA_GetRHAVersion = NULL;
    g_MUX_RHAFunctionList.pfnRHA_ConfirmAudioMode = NULL;
    g_MUX_RHAFunctionList.pfnRHA_SystemPowerChange = NULL;
    g_MUX_hRHAInst = LoadLibrary(TEXT("RHA.DLL"));
    if(g_MUX_hRHAInst == NULL)
    {
        SENDERR((TEXT("[RIL MUX7010] Error: Failed in load RHA.DLL\n")));
    }
    else
    {
        g_MUX_RHAFunctionList.pfnRHA_Init = (PFNRHA_INIT_DEINIT)GetProcAddress(g_MUX_hRHAInst, RHA_INIT);
        g_MUX_RHAFunctionList.pfnRHA_DeInit = (PFNRHA_INIT_DEINIT)GetProcAddress(g_MUX_hRHAInst, RHA_DEINIT);
        g_MUX_RHAFunctionList.pfnRHA_RadioPwrOn = (PFNRHA_RADIO_POWER_ON)GetProcAddress(g_MUX_hRHAInst, RHA_RADIO_POWER_ON);
        g_MUX_RHAFunctionList.pfnRHA_RadioPwrOff = (PFNRHA_RADIO_POWER_OFF)GetProcAddress(g_MUX_hRHAInst, RHA_RADIO_POWER_OFF);
        g_MUX_RHAFunctionList.pfnRHA_IsRadioPwrOn = (PFNRHA_IS_RADIO_POWER_ON)GetProcAddress(g_MUX_hRHAInst, RHA_IS_RADIO_POWER_ON);
        g_MUX_RHAFunctionList.pfnRHA_SystemPowerChange = (PFNRHA_SYSTEM_POWER_CHANGE)GetProcAddress(g_MUX_hRHAInst, RHA_SYSTEM_POWER_CHANGE);
    }
    
    if (g_MUX_RHAFunctionList.pfnRHA_Init != NULL)
       {
        g_MUX_RHAFunctionList.pfnRHA_Init();
        SENDLOG((TEXT("[RIL MUX7010]:pfnRHA_Init Completed \n")));        
       }
    else
    {
        SENDERR((TEXT("[RIL MUX7010]:Failed in pfnRHA_Init == NULL \n")));        
    }        

#ifndef GSM0710_UNITARY_TESTS
    DCB        dcb;
    char sTmpBuf[AT_CMD_SIZE];
    char *pCurChar = NULL;
    ULONG    len = 0;
    DWORD dwMask = 0;

    int iNbTimeOut = 0, iNbChar = 0;
    bool bOkReceived = FALSE;

    // open COM port if necessary
    //
    // Rem : the COM port is closed upon CDispatcher destruction
    if (INVALID_HANDLE_VALUE == m_hComPort)
    {
        m_hComPort = CreateFile(szComName, GENERIC_READ | GENERIC_WRITE,
                                0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (INVALID_HANDLE_VALUE == m_hComPort)
        {
            goto error;
        }

        // Set Baudrate
        dcb.DCBlength = sizeof(DCB);

        if (!GetCommState(m_hComPort, &dcb))
        {
            goto error;        
        }

        // Set com port
        dcb.fBinary = TRUE;
        dcb.fParity = TRUE;
        dcb.fOutxCtsFlow = TRUE;
        dcb.fOutxDsrFlow = FALSE;
        dcb.fDtrControl = DTR_CONTROL_DISABLE;
        dcb.fDsrSensitivity = FALSE;
        dcb.fTXContinueOnXoff = TRUE;
        dcb.fOutX = FALSE;
        dcb.fInX = FALSE;
        dcb.fErrorChar = FALSE;
        dcb.fNull = FALSE;
        dcb.fRtsControl = RTS_CONTROL_ENABLE; //RTS_CONTROL_HANDSHAKE;
        dcb.fAbortOnError = FALSE;
        dcb.ByteSize    = 8;
        dcb.Parity        = NOPARITY;
        dcb.StopBits    = ONESTOPBIT;
        dcb.BaudRate = RIL_MUX_BEST_BAUDRATE;
    
        if (!SetCommState(m_hComPort, &dcb))
        {
            goto error;
        }

        if ( !SetCommMask(m_hComPort, EV_BREAK | EV_ERR | EV_RXCHAR) )
        {
            goto error;
        }

        // no time-out
        COMMTIMEOUTS cto;
        memset(&cto, 0, sizeof(COMMTIMEOUTS));
        cto.ReadIntervalTimeout = MAXDWORD;
        cto.WriteTotalTimeoutConstant = GSM0710_T3;
        SetCommTimeouts(m_hComPort, &cto);
    }

    // Switch radio on
    if (!m_bResetRequired)
    {
        SENDLOG((TEXT("710MUX: Dispatcher: -> SwitchRadioOn\r\n")));
        if( !SwitchRadioOn())
        {
            goto error;
        }
    }
    else 
    {
        SENDLOG((TEXT("710MUX: Dispatcher: -> ResetRadio\r\n")));
        if (!ResetRadio())
        {
            goto error;
        }
    }

    Sleep(4000);
    
    // Modem Init for Siemens MC
    sprintf(sTmpBuf, "AT\r");
    WriteFile(m_hComPort, sTmpBuf, strlen(sTmpBuf), &len, NULL);

    // wait for TE's answer with time-out
    m_dwTimeOut = GSM0710_T3;
    iNbTimeOut = 3;
    iNbChar = AT_CMD_SIZE - 1;
    bOkReceived = false;
    dwMask = 0;

    pCurChar = sTmpBuf;
    memset(sTmpBuf, 0, AT_CMD_SIZE);
    while(iNbTimeOut && iNbChar && !bOkReceived )
    {
        SetEvent(m_hLaunchTimeOutEv);
        if (WaitCommEvent(m_hComPort, &dwMask, NULL) && (EV_RXCHAR & dwMask))
        {
            // cancel timeout
            PulseEvent(m_hCancelTimeOutEv);

            // get response and throw it away (if we've been able to read => it works)
            if ( ReadFile(m_hComPort, pCurChar, iNbChar, &len, NULL) && len >= 0)
            {
                pCurChar += len;
                iNbChar -= len;
                if (NULL != strstr(sTmpBuf, OK_RSP))
                {
                    bOkReceived = true;
                }
            }
        }
        else
        {
            // time-out on read
            DWORD dwErrors;

            if (EV_ERR & dwMask)
            {
                ClearCommError(m_hComPort, &dwErrors, NULL);
            }

            if (EV_BREAK & dwMask)
            {
                ClearCommBreak(m_hComPort);
            }

            iNbTimeOut--;
            SENDERR((TEXT("710MUX: Dispatcher: -> Time out\r\n")));
        }
    }

    if ( !bOkReceived )
    {
        // MS is not responding 
        SENDERR((TEXT("710MUX: Dispatcher: -> Init, ERROR no rsp on AT\r\n")));
        

        if (!ConfigureModemBaudRate(RIL_MUX_BEST_BAUDRATE))
        {
            SENDERR((TEXT("710MUX: Dispatcher: -> Try to configure Modem baudrate to %d. Failed \r\n"), RIL_MUX_BEST_BAUDRATE));        
        }
        else
        {
            SENDERR((TEXT("710MUX: Dispatcher: -> Configure Modem baudrate to %d. Completed. \r\n"), RIL_MUX_BEST_BAUDRATE));        
            SENDERR((TEXT("[RIL][710MUX]: Please reboot device to make Modem baudrate changed valid \r\n")));        
        }
        
        goto error;
    }

    SENDERR((TEXT("710MUX: Dispatcher: -> Init, AT Successful\r\n")));

    // set fix baud rate on the Modem side (if necessary)
    // use GSM 07.07 +IPR AT command (V25ter standard)
    sprintf(sTmpBuf, "AT+IPR=%u\r", dcb.BaudRate);
    WriteFile(m_hComPort, sTmpBuf, strlen(sTmpBuf), &len, NULL);

    // wait for TE's answer with time-out
    m_dwTimeOut = GSM0710_T3;
    iNbTimeOut = 3;
    iNbChar = AT_CMD_SIZE - 1;
    bOkReceived = false;
    dwMask = 0;

    pCurChar = sTmpBuf;
    memset(sTmpBuf, 0, AT_CMD_SIZE);
    while(iNbTimeOut && iNbChar && !bOkReceived )
    {
        SetEvent(m_hLaunchTimeOutEv);
        if (WaitCommEvent(m_hComPort, &dwMask, NULL) && (EV_RXCHAR & dwMask))
        {
            // cancel timeout
            PulseEvent(m_hCancelTimeOutEv);

            // get response and throw it away (if we've been able to read => it works)
            if ( ReadFile(m_hComPort, pCurChar, iNbChar, &len, NULL) && len >= 0)
            {
                pCurChar += len;
                iNbChar -= len;
                if (NULL != strstr(sTmpBuf, OK_RSP))
                {
                    bOkReceived = true;
                }
            }
        }
        else
        {
            // time-out on read
            DWORD dwErrors;

            if (EV_ERR & dwMask)
            {
                ClearCommError(m_hComPort, &dwErrors, NULL);
            }

            if (EV_BREAK & dwMask)
            {
                ClearCommBreak(m_hComPort);
            }

            iNbTimeOut--;
        }
    }

    if ( !bOkReceived )
    {
        // MS is not responding 
        SENDERR((TEXT("710MUX: Dispatcher: -> Init, ERROR no rsp on AT+IPR\r\n")));
        goto error;
    }

    SENDERR((TEXT("710MUX: Dispatcher: -> Init, AT+IPR Successful\r\n")));
    
    // set At&S0 Cmd
    sprintf(sTmpBuf, "AT&S0\r");
    WriteFile(m_hComPort, sTmpBuf, strlen(sTmpBuf), &len, NULL);

    // wait for TE's answer with time-out
    m_dwTimeOut = GSM0710_T3;
    iNbTimeOut = 3;
    iNbChar = AT_CMD_SIZE - 1;
    bOkReceived = false;
    dwMask = 0;

    pCurChar = sTmpBuf;
    memset(sTmpBuf, 0, AT_CMD_SIZE);
    while(iNbTimeOut && iNbChar && !bOkReceived )
    {
        SetEvent(m_hLaunchTimeOutEv);
        if (WaitCommEvent(m_hComPort, &dwMask, NULL) && (EV_RXCHAR & dwMask))
        {
            // cancel timeout
            PulseEvent(m_hCancelTimeOutEv);

            // get response and throw it away (if we've been able to read => it works)
            if ( ReadFile(m_hComPort, pCurChar, iNbChar, &len, NULL) && len >= 0)
            {
                pCurChar += len;
                iNbChar -= len;
                if (NULL != strstr(sTmpBuf, OK_RSP))
                {
                    bOkReceived = true;
                }
            }
        }
        else
        {
            // time-out on read
            DWORD dwErrors;

            if (EV_ERR & dwMask)
            {
                ClearCommError(m_hComPort, &dwErrors, NULL);
            }

            if (EV_BREAK & dwMask)
            {
                ClearCommBreak(m_hComPort);
            }

            iNbTimeOut--;
        }
    }

    if ( !bOkReceived )
    {
        // MS is not responding 
        SENDERR((TEXT("710MUX: Dispatcher: -> Init, ERROR no rsp on AT&S0 \r\n")));
        goto error;
    }

    SENDERR((TEXT("710MUX: Dispatcher: -> Init, AT&S0 Successful\r\n")));

    // set AT+ICF=3 Cmd to set (8 data 0 parity 1 stop) for frame date transfer
    sprintf(sTmpBuf, "AT+ICF=3\r");
    WriteFile(m_hComPort, sTmpBuf, strlen(sTmpBuf), &len, NULL);

    // wait for TE's answer with time-out
    m_dwTimeOut = GSM0710_T3;
    iNbTimeOut = 3;
    iNbChar = AT_CMD_SIZE - 1;
    bOkReceived = false;
    dwMask = 0;

    pCurChar = sTmpBuf;
    memset(sTmpBuf, 0, AT_CMD_SIZE);
    while(iNbTimeOut && iNbChar && !bOkReceived )
    {
        SetEvent(m_hLaunchTimeOutEv);
        if (WaitCommEvent(m_hComPort, &dwMask, NULL) && (EV_RXCHAR & dwMask))
        {
            // cancel timeout
            PulseEvent(m_hCancelTimeOutEv);

            // get response and throw it away (if we've been able to read => it works)
            if ( ReadFile(m_hComPort, pCurChar, iNbChar, &len, NULL) && len >= 0)
            {
                pCurChar += len;
                iNbChar -= len;
                if (NULL != strstr(sTmpBuf, OK_RSP))
                {
                    bOkReceived = true;
                }
            }
        }
        else
        {
            // time-out on read
            DWORD dwErrors;

            if (EV_ERR & dwMask)
            {
                ClearCommError(m_hComPort, &dwErrors, NULL);
            }

            if (EV_BREAK & dwMask)
            {
                ClearCommBreak(m_hComPort);
            }

            iNbTimeOut--;
        }
    }

    if ( !bOkReceived )
    {
        // MS is not responding 
        SENDERR((TEXT("710MUX: Dispatcher: -> Init, ERROR no rsp on AT+ICF=3\r\n")));
        goto error;
    }

    SENDERR((TEXT("710MUX: Dispatcher: -> Init, AT+ICF=3 Successful\r\n")));    
    
    // set At Q3 Cmd
    sprintf(sTmpBuf, "AT\\Q3\r");
    WriteFile(m_hComPort, sTmpBuf, strlen(sTmpBuf), &len, NULL);

    // wait for TE's answer with time-out
    m_dwTimeOut = GSM0710_T3;
    iNbTimeOut = 3;
    iNbChar = AT_CMD_SIZE - 1;
    bOkReceived = false;
    dwMask = 0;

    pCurChar = sTmpBuf;
    memset(sTmpBuf, 0, AT_CMD_SIZE);
    while(iNbTimeOut && iNbChar && !bOkReceived )
    {
        SetEvent(m_hLaunchTimeOutEv);
        if (WaitCommEvent(m_hComPort, &dwMask, NULL) && (EV_RXCHAR & dwMask))
        {
            // cancel timeout
            PulseEvent(m_hCancelTimeOutEv);

            // get response and throw it away (if we've been able to read => it works)
            if ( ReadFile(m_hComPort, pCurChar, iNbChar, &len, NULL) && len >= 0)
            {
                pCurChar += len;
                iNbChar -= len;
                if (NULL != strstr(sTmpBuf, OK_RSP))
                {
                    bOkReceived = true;
                }
            }
        }
        else
        {
            // time-out on read
            DWORD dwErrors;

            if (EV_ERR & dwMask)
            {
                ClearCommError(m_hComPort, &dwErrors, NULL);
            }

            if (EV_BREAK & dwMask)
            {
                ClearCommBreak(m_hComPort);
            }

            iNbTimeOut--;
        }
    }

    if ( !bOkReceived )
    {
        // MS is not responding 
        SENDERR((TEXT("710MUX: Dispatcher: -> Init, ERROR no rsp on AT Q3\r\n")));
        goto error;
    }

    SENDERR((TEXT("710MUX: Dispatcher: -> Init, AT Q3 Successful\r\n")));
    
    // enable GSM 07.10 => send MUX cmd 
    //
    // AT+CMUX=
    // 0
    sprintf(sTmpBuf, "AT+CMUX=0\r");
    //sprintf(sTmpBuf, "AT&S0+CMUX=0\r");
    //sprintf(sTmpBuf, "AT+CMUX=0,0,5,98,10,3,30,10\r");
    
    WriteFile(m_hComPort, sTmpBuf, strlen(sTmpBuf), &len, NULL);

    // wait for TE's answer with time-out
    bOkReceived = false;
    iNbTimeOut = 2;
    iNbChar = AT_CMD_SIZE - 1;
    dwMask = 0;

    pCurChar = sTmpBuf;
    memset(sTmpBuf, 0, AT_CMD_SIZE);

    while(iNbTimeOut && iNbChar && !bOkReceived)
    {
        SetEvent(m_hLaunchTimeOutEv);
        if (WaitCommEvent(m_hComPort, &dwMask, NULL) && (EV_RXCHAR & dwMask))
        {
            // cancel timeout
            PulseEvent(m_hCancelTimeOutEv);

            // get response and throw it away (if we've been able to read => it works)
            if ( ReadFile(m_hComPort, pCurChar, iNbChar, &len, NULL) && len >= 0)
            {
                pCurChar += len;
                iNbChar -= len;
                if (NULL != strstr(sTmpBuf, OK_RSP))
                {
                    bOkReceived = true;
                }
            }
        }
        else
        {
            // time-out on read
            DWORD dwErrors;

            if (EV_ERR & dwMask)
            {
                ClearCommError(m_hComPort, &dwErrors, NULL);
            }

            if (EV_BREAK & dwMask)
            {
                ClearCommBreak(m_hComPort);
            }

            iNbTimeOut--;
        }
    }

    if (!bOkReceived)
    {
        // not OK response
        SENDERR((TEXT("710MUX: Dispatcher: -> Init, ERROR on AT+CMUX\r\n")));
        goto error;
    }

    SENDERR((TEXT("710MUX: Dispatcher: -> Init, AT+CMUX Successful\r\n")));    
    
    //Sleep(300);

    //--------------------------------------------------
#else
    // Unitary tests => used faked COM port
    m_hComPort = hFakeCOMPort;
#endif    // GSM0710_UNITARY_TESTS


    SENDLOG((TEXT("710MUX: CDispatcher - Init open underlying COM port OK\r\n")));

#if 0
    BYTE aucData[6] = {0xF9, 0x03, 0x3F, 0x1, 0x0, 0xF9};

    BYTE bFCS = TmpCalcFCS(aucData + 1, 3);
    aucData[4] = bFCS;

    WriteFile(m_hComPort, (LPCVOID)aucData, sizeof(aucData), &len, NULL);

    // wait for TE's answer with time-out
    bOkReceived = false;
    iNbTimeOut = 2;
    iNbChar = AT_CMD_SIZE - 1;

    pCurChar = sTmpBuf;
    memset(sTmpBuf, 0, AT_CMD_SIZE);

    while(iNbTimeOut && iNbChar && !bOkReceived)
    {
        SetEvent(m_hLaunchTimeOutEv);

        if (FALSE == WaitCommEvent(m_hComPort, &dwMask, NULL))
        {
            goto error; 
        }
        
        if ( EV_RXCHAR & dwMask)
        {
            // cancel timeout
            PulseEvent(m_hCancelTimeOutEv);

            // get response and throw it away (if we've been able to read => it works)
            if ( ReadFile(m_hComPort, pCurChar, iNbChar, &len, NULL) && len >= 0)
            {
                pCurChar += len;
                iNbChar -= len;
                if (NULL != strstr(sTmpBuf, OK_RSP))
                {
                    bOkReceived = true;
                }
            }
        }
        else
        {
            // time-out on read
            DWORD dwErrors;

            if (EV_ERR & dwMask)
            {
                ClearCommError(m_hComPort, &dwErrors, NULL);
            }

            if (EV_BREAK & dwMask)
            {
                ClearCommBreak(m_hComPort);
            }

            iNbTimeOut--;
        }
    }

    if (!bOkReceived)
    {
        // not OK response
        SENDERR((TEXT("710MUX: Dispatcher: -> Init,SABM Error\r\n")));
        goto error;
    }
    else
    {
        SENDERR((TEXT("710MUX: Dispatcher: -> Init, SABM Pass\r\n")));
    }
        

#endif

    // Dispatcher is ready to operate
    //---------------------------------
    m_bIsOk = true;
    m_bIsInitialized = true;
    m_lPowerState = OPERATIONAL;
    SetEvent(m_hGoAheadEv);



    // Create GSM 07.10 control channel
    //---------------------------------
    m_pCtrlChanel = new CCtrlChanel(CTRL_CHANEL_DLCI);
    if (NULL == m_pCtrlChanel || !*m_pCtrlChanel)
    {
        goto error;
    }

    // normal return
    //---------------
    return true;

error:
    // error case return
    //-------------------
    TerminateAll();

    return false;
}

bool CDispatcher::ReinitAll()
{
    // init all the registered channels
    //
    // rem : ctrl channel and dispatcher will be automatically initialized
    for (long i = 1; i < MAX_CHANEL_ID -1; i++)
    {
        if ( NULL != m_ppChanel[i] )
        {
            m_ppChanel[i]->Init();
        }
    }

    // Init is OK if ctrl channel is OK
    // => at least 1 user channel is connected
    if ( !*m_ppChanel[0] )
    {
        return false;
    }
    else
    {
        return true;
    }
    
}

//--------------------------------
// Terminate
//  - Terminate must be used 
//    when it is a temporary termination 
//      of the MUX, e.g.: flight mode
//
//    - TerminateAll must be used 
//    when it is a complete termination
//    of the MUX, e.g.: object destruction, init failure
//     
//--------------------------------------------------------
void CDispatcher::TerminateAll() 
{
    Terminate(); 
    TerminateSerialIntf();

    if(g_MUX_hRHAInst != NULL) {
        if (g_MUX_RHAFunctionList.pfnRHA_DeInit != NULL)
            g_MUX_RHAFunctionList.pfnRHA_DeInit();
        FreeLibrary(g_MUX_hRHAInst);
        g_MUX_hRHAInst = NULL;
    }
}

void CDispatcher::TerminateSerialIntf()
{
    // terminate object, handles, ... 
    // that are not terminated at terminate time
    m_bIsInitialized = false;
    m_bTerminateAll = true;

    // terminate threads
    //-------------------
    if ( NULL != m_hTimeOutTh)
    {
        // wake up thread
        SetEvent(m_hCancelTimeOutEv);
        SetEvent(m_hLaunchTimeOutEv);

        // wait end of thread
        WaitForSingleObject(m_hTimeOutTh, INFINITE);
        CloseHandle(m_hTimeOutTh); 
        m_hTimeOutTh = NULL;
    }

    // close event handles
    //----------------------
    if (NULL != m_hLaunchTimeOutEv)
    {
        CloseHandle(m_hLaunchTimeOutEv);
    }

    if (NULL != m_hCancelTimeOutEv)
    {
        CloseHandle(m_hCancelTimeOutEv);
    }

    m_hLaunchTimeOutEv = NULL;
    m_hCancelTimeOutEv = NULL;

#ifndef GSM0710_UNITARY_TESTS
    // close DownStream COM port
    //----------------------------
    if (INVALID_HANDLE_VALUE != m_hComPort) 
    {
        CloseHandle(m_hComPort);
        m_hComPort = INVALID_HANDLE_VALUE;
    }
#endif    // GSM0710_UNITARY_TESTS
}

void CDispatcher::TryTerminate() 
{
    // terminate only if 
    // - only the control channel is still active
    // - the dispatcher is not currently terminating

    if (m_bIsOk && 1 == m_lConnectedChanelNb)
    {
        Terminate();
    }
}

bool CDispatcher::IsOnlyCtrlChannelOpen()
{
    bool bFlag = TRUE;
    
    //Check if there is any live channel except ctrl channel
    // rem : must terminate ctrl channel at the end of the loop
    for (long i = MAX_CHANEL_ID -1; i >=1; i--)
    {
        if ( NULL != m_ppChanel[i] )
        {
            SENDLOG((TEXT("710MUX: CDispatcher::IsOnlyCtrlChannelOpen: dlci %d is alive\r\n"), i));
            bFlag = FALSE;
            break;
        }
    }

    return bFlag;    
}

void CDispatcher::Terminate()
{
    // dispatcher is no longer usable
    //--------------------------------
    m_bIsOk = false;
    m_lPowerState = OPERATIONAL;

    // terminate all of the opened chanels
    //-------------------------------------

    // terminate loop with control chanel
    //
    // rem : must terminate ctrl channel at the end of the loop
    for (long i = MAX_CHANEL_ID -1; i >=0; i--)
    {
        if ( NULL != m_ppChanel[i] )
        {
            m_ppChanel[i]->Terminate();
        }
    }

    // terminate threads
    //-------------------
    m_bTerminate = true;

    if (NULL != m_hSenseComTh)
    {
        // wake up thread
        SetEvent(m_hGoAheadEv);

#ifndef GSM0710_UNITARY_TESTS
       SetCommMask(m_hComPort, DEFAULT_COM_MASK);
#else
        // unitary tests => faked serial line
        SetEvent(hUTest_FakeRxEv);
#endif

        // wait end of thread
        WaitForSingleObject(m_hSenseComTh, INFINITE);
        CloseHandle(m_hSenseComTh); 
        m_hSenseComTh    = NULL;
    }

    if ( NULL != m_hWakeUpTh)
    {
        // wake up thread
        SetEvent(m_hLaunchWakeUpEv);
        SetEvent(m_hWokenUpEv);

        // wait end of thread
        WaitForSingleObject(m_hWakeUpTh, INFINITE);
        CloseHandle(m_hWakeUpTh); 
        m_hWakeUpTh = NULL;
    }

    // close event handles
    //----------------------
    if (NULL != m_hGoAheadEv)
    {
        CloseHandle(m_hGoAheadEv);
        m_hGoAheadEv = NULL;
    }

    if (NULL != m_hWokenUpEv)
    {
        CloseHandle(m_hWokenUpEv);
        m_hWokenUpEv = NULL;
    }

    if (NULL != m_hLaunchWakeUpEv) 
    {
        CloseHandle(m_hLaunchWakeUpEv);
        m_hLaunchWakeUpEv = NULL;
    }

    if (NULL != m_hRebootOKEv)
    {
        CloseHandle(m_hRebootOKEv);
        m_hRebootOKEv = NULL;
    }

    if (NULL != m_hRebootKOEv)
    {
        CloseHandle(m_hRebootKOEv);
        m_hRebootKOEv = NULL;
    }

    // delete critical section
    //--------------------------
    DeleteCriticalSection(&m_csSend);

    // delete created object and free memory
    //---------------------------------------
    if ( NULL != m_pCtrlChanel)
    {
        delete m_pCtrlChanel;
        m_pCtrlChanel = NULL;
    }

    // reset related data
    //
    // rem : must be done after
    //       delete ctrl channel 
    //-----------------------------
    if(NULL != m_hCancelResetEv)
    {
        CloseHandle(m_hCancelResetEv);
        m_hCancelResetEv = NULL;
    }
}

//---------------------------------------------------------------
// ! operator 
//        - indicates if Dispatcher instance is ready to be used
//---------------------------------------------------------------
bool CDispatcher::operator!()
{
    return (!m_bIsOk);
}

//-----------------------------------
// register new chanel to dispatcher
//-----------------------------------
bool CDispatcher::Register(CChanel *pChnl, tFrameParam* pFP)
{
    BYTE bChanelId;

    // get chanel DLCI
    bChanelId = pChnl->GetDLCI() ;
    if (NULL  != m_ppChanel[bChanelId] && 
        pChnl != m_ppChanel[bChanelId] )
    {
        // chanel with same Id already exist
        goto error;
    }

    // save new chanel object
    m_ppChanel[bChanelId] = pChnl;

    if (pFP)
    {
        pFP->bFlag = m_bFrameFlag;
        pFP->dwMaxDataSize = m_cbMaxFrameData;
    }

    SENDLOG((TEXT("710MUX: CDispatcher::Register: chanel %s, dlci %i\r\n"), pChnl->m_pName, bChanelId));

    return true;

error:
    return false; 
}

//-------------------------------------
// unregister new chanel to dispatcher
//-------------------------------------
bool CDispatcher::Unregister(CChanel *pChnl)
{
    BYTE bChanelId;

    // get chanel DLCI
    bChanelId = pChnl->GetDLCI() ;
    if (NULL == m_ppChanel[bChanelId])
    {
        goto error;
    }

    // save new chanel object
    m_ppChanel[bChanelId] = NULL;

    return true;

error:
    return false; 
}

//----------------------------------------------
// check if this channel is already registered
//----------------------------------------------
bool CDispatcher::IsDlciAlreadyBooked(CChanel *pChnl)
{
    BYTE bChanelId;

    // get chanel DLCI
    bChanelId = pChnl->GetDLCI() ;    
    if (NULL  != m_ppChanel[bChanelId] && 
        pChnl != m_ppChanel[bChanelId])
    {
        // chanel with same Id already exist
        return true;
    }
    else
    {
        return false;
    }
}

//---------------------------------------------
// Service negociation 
//---------------------------------------------
bool CDispatcher::NegotiateService(BYTE bDlci)
{
    return m_pCtrlChanel->NegotiateService(bDlci);
}

//---------------------------------------------
// modem status 
//---------------------------------------------
bool CDispatcher::SetModemStatus(BYTE bDlci, BYTE v24Sig)
{
    bool Ret = false;

    if (NULL  != m_ppChanel[bDlci])
    {
        m_ppChanel[bDlci]->SetModemStatus(v24Sig);
        Ret = true;
    }

    return Ret;            
}

bool CDispatcher::SendModemStatus(BYTE bDlci, BYTE v24Sig)
{
    return m_pCtrlChanel->SendModemStatus(bDlci, v24Sig);
}

//---------------------------------------------
// signal power saving mode to modem 
//---------------------------------------------
bool CDispatcher::EnterPowerSavingMode()
{
    if (!m_bIsOk || POWER_SAVING == m_lPowerState)
    {
        // modem already in power saving mode or off
        return true;
    }
    else
    {
        // assume that WinCE will not go to Sleep mode if any activity on serial line
        // (any wake up procedure curremtly running)
        return m_pCtrlChanel->EnterPowerSavingMode();
    }
}

//-------------
// Send bytes
//-------------
DWORD CDispatcher::Send(BYTE* pFrame, DWORD cbFrame)
{
    DWORD dwSentBytes = 0;   
    LONG lPowerStateTmp;

    // untitary test enter macro
    UTest_SEND_ENTER();

    // -> lock send
    LockSend();

    // check power saving state
    InterlockedExchange(&lPowerStateTmp, m_lPowerState); 
    if (POWER_SAVING == lPowerStateTmp)
    {
        SENDLOG((TEXT("710MUX: Dispatcher::Send: -> wake up TE\r\n")));

        // Launch wake up procedure (WakeUpTh) 
        // and wait for the procedure to be complete
        //

        //    - m_hGoAheadEv must be reseted because it could have been
        //      signaled at end of power up procedure initiated by TE
        //      and therefore not consume by this wait
        ResetEvent(m_hGoAheadEv);
        SetEvent(m_hLaunchWakeUpEv);
        WaitForSingleObject(m_hGoAheadEv, INFINITE);

        InterlockedExchange(&lPowerStateTmp, m_lPowerState); 
        if ( WAKE_UP_TIME_OUTED == lPowerStateTmp )
        {
            SENDLOG((TEXT("710MUX: Dispatcher::Send: -> cannot wake TE up\r\n")));

            // wait for a new frame and still in power saving mode
            InterlockedExchange(&m_lPowerState, POWER_SAVING); 

            // all wake up attempt have failed
            // => return error
            // set return value and error code
            SetLastError(ERROR_NOT_READY);
            goto error;
        }
    }

    // send the bytes
    if (FALSE == WriteFile(m_hComPort, pFrame, cbFrame, &dwSentBytes, NULL))
    {
        SENDLOG((TEXT("710MUX: Dispatcher::Send: -> WriteFile Error\r\n")));        
    }

    SENDLOG((TEXT("710MUX: Dispatcher::Send: -> cbFrame = %d, dwSentBytes = %d \r\n"), 
                  cbFrame, dwSentBytes));                

    // fall through
error:

    // <- unlock send
    UnlockSend();

    // unitary test leave macro
    UTest_SEND_LEAVE();

    return dwSentBytes;
}

void CDispatcher::WakeupResponse()
{
    if (OPERATIONAL == m_lPowerState)
    {
        //    this needs to be done since entering a 
        //    wake up procedure is the only way to recover
        //    if the MUX driver has for any reason missed 
        //    the previous PSC command from the Modem
        //    (see GSM 07.10 - power management)
        m_lPowerState = POWER_SAVING;
    }

    //Ignore if already in progress
    if (m_lPowerState != WAKING_UP_TE_INITIATIVE &&
        m_lPowerState != WAKING_UP_MS_INITIATIVE)
    {
        // check if 
        //    - the MUX driver wakes the TE up 
        //    - or the TE that wakes itself up
        if (TryEnterCriticalSection(&m_csSend))
        {
            // no thread was owning the Send lock
            // => MUX hasn't woken the TE up
            //
            // Rem : the send operation should be blocked
            //         until the TE has sent a frame.
            //         Critical section will be released
            //         upon address reception
            SENDLOG((TEXT("710MUX: CDispatcher::ParseRecBytes: -> TE wake up itself\r\n")));

            // start the wake up thread
            ResetEvent(m_hGoAheadEv);
            SetEvent(m_hLaunchWakeUpEv);

            // TE is in wake up procedure
            m_lPowerState = WAKING_UP_TE_INITIATIVE;
        }
        else
        {
            // Send method is already owning the Send lock
            // => MUX is trying to wake the TE up

            // let the wake up thread know that TE is operational
            m_lPowerState = WAKING_UP_MS_INITIATIVE;
            PulseEvent(m_hWokenUpEv);

            // let time to the wake up thread to react (if any active) 
            WaitForSingleObject(m_hGoAheadEv, WAKE_UP_SEND_FLAG_INTERVAL);
        }
    }
}

DWORD CDispatcher::ReadAdvancedModeFrame (void* pParseParam, BYTE* pRxBuffer, DWORD cbRxBuffer)
{
    DWORD cbRead = 0;
    BYTE byte;

    tParseData* pPD = (tParseData*)pParseParam;
    BYTE* pCopyBuf = pPD->pFrameBuf + pPD->cbData;   

    while (cbRxBuffer--)
    {
        byte = *pRxBuffer++;        
        cbRead++;

        if (byte == m_bFrameFlag)
        {
            if (pPD->cbData >= FRAME_SIZE_MIN && 
                (pPD->pFrameBuf[0] & SET_EA_MASK) &&
                ValidControlField(pPD->pFrameBuf[1]))
            {
                pPD->State = READ_FRAME_COMPLETE;    
            }
            else
            {
                SENDERR((TEXT("710MUX: CDispatcher::ReadAdvancedModeFrame: invalid frame A=0x%02X C=0x%02x L=%u\r\n"),
                        pPD->pFrameBuf[0], pPD->pFrameBuf[1], pPD->cbData));
                pPD->State = READ_FRAME_ERROR;
            }
            break;
        }
        else if (GSM0710_CTRL_OCTET == byte)
        {
            pPD->Param.bEsc = TRUE;
        }
        else
        {
            if (pPD->cbData < pPD->cbFrameBuf)
            {
                if (pPD->Param.bEsc)
                {
                    byte ^= 0x20;
                    pPD->Param.bEsc = FALSE;
                }
                *pCopyBuf++ = byte;
                pPD->cbData++;
            }
            else
            {
                SENDERR((TEXT("710MUX: CDispatcher::ReadAdvancedModeFrame: frame buffer is full\r\n")));
                pPD->State = READ_FRAME_ERROR;
                cbRead--;
                break;
            }
        }
    }

    return cbRead;
}

DWORD CDispatcher::ReadBasicModeFrame (void* pParseParam, BYTE* pRxBuffer, DWORD cbRxBuffer)
{
    DWORD cbRead = 0;
    BYTE byte;

    tParseData* pPD = (tParseData*)pParseParam;
    BYTE* pCopyBuf = pPD->pFrameBuf + pPD->cbData;  

    while (cbRxBuffer--)
    {
        byte = *pRxBuffer++;        
        cbRead++;
        
        if (READ_FRAME_HEADER == pPD->State)
        {
            if (FRAME_HEADER_SIZE == pPD->cbData)
            {
                //Received address, control and 1st byte of length
                
                //Validate the header
                if (!(pPD->pFrameBuf[0] & SET_EA_MASK) ||
                    m_ppChanel[GET_DLCI(pPD->pFrameBuf[0])] == NULL ||
                    !ValidControlField(pPD->pFrameBuf[1]))
                {
                    SENDERR((TEXT("710MUX: CDispatcher::ReadBasicModeFrame: Invalid header A=0x%02X C=0x%02X\r\n"), 
                            pPD->pFrameBuf[0], pPD->pFrameBuf[1]));

                    pPD->State = READ_FRAME_ERROR;
                    break;
                }

                //Read length

                pPD->Param.cbLength = (DWORD)(byte >> 1);
                
                if (byte & SET_EA_MASK)
                {
                    //Length is 1 byte long. Validate.
                    if (pPD->Param.cbLength > m_cbMaxFrameData)
                    {
                        SENDERR((TEXT("710MUX: CDispatcher::ReadBasicModeFrame: Invalid length L=%u\r\n"), pPD->Param.cbLength));
                        pPD->State = READ_FRAME_ERROR;
                        break;
                    }
                    else
                    {
                        pPD->State = READ_FRAME_DATA;
                    }
                }
                else
                {
                    //Extended length: need one more byte
                    pPD->State = READ_FRAME_LENGTH;                   
                }

                continue;
            }
        }
        else if (READ_FRAME_LENGTH == pPD->State)
        {
            pPD->Param.cbLength |= (((DWORD)byte) << 7);

            if (pPD->Param.cbLength > m_cbMaxFrameData)
            {
                SENDERR((TEXT("710MUX: CDispatcher::ReadBasicModeFrame: Invalid length L=%u\r\n"), pPD->Param.cbLength));
                pPD->State = READ_FRAME_ERROR;
                break;
            }
            else
            {
                pPD->State = READ_FRAME_DATA;
            }

            continue;
        }
        else
        {
            if (pPD->cbData == pPD->Param.cbLength + FRAME_SIZE_MIN)
            {
                //Frame has been received.
                //Verify that it terminates with a flag
                if (byte == m_bFrameFlag)
                {
                    pPD->State = READ_FRAME_COMPLETE;
                }
                else
                {
                    SENDERR((TEXT("710MUX: CDispatcher::ReadBasicModeFrame: invalid frame (no flag)\r\n")));
                    pPD->State = READ_FRAME_ERROR;
                }
                break;
            }            
        }

        if (pPD->cbData < pPD->cbFrameBuf)
        {
            *pCopyBuf++ = byte;
            pPD->cbData++;            
        }
        else
        {
            SENDERR((TEXT("710MUX: CDispatcher::ReadBasicModeFrame: frame buffer is full\r\n")));
            pPD->State = READ_FRAME_ERROR;            
            break;
        }
    }

    return cbRead;
}

//--------------------------------------------------------------
// received bytes parser
//--------------------------------------------------------------
void CDispatcher::ParseRecBytes(tParseData* pPD, BYTE* pRxBuffer, DWORD cbRxBuffer)
{
    while (cbRxBuffer)
    {
        // check parser state
        switch (pPD->State)
        {
            case WAIT_FRAME:
            {                
                // Detect beginning of a frame                
                // A frame may start either with its own opening flag or with 
                // the closing flag of the previous frame      
                if (*pRxBuffer == m_bFrameFlag)
                {
                    if (pPD->bEndOfFrame)
                    {
                        pPD->State = START_FRAME;
                        pPD->bEndOfFrame = FALSE;
                    }
                    else
                    {  
                        pPD->bEndOfFrame = TRUE;    
                    }
                }
                else
                {
                    if (pPD->bEndOfFrame)
                    {
                        pPD->State = START_FRAME;
                        pPD->bEndOfFrame = FALSE;
                        continue;
                    }
                }
            }
            break;

            case START_FRAME:
            {
                if (*pRxBuffer == m_bFrameFlag)
                {
                    // flag has been received instead of address => wake up frame
                    // respond if necessary and keep waiting for a valid address
                    WakeupResponse();
                }
                else
                {
                    // valid frame starts => stop wake-up procedure (if necessary)
                    switch(m_lPowerState)
                    {
                        case POWER_SAVING:
                        case WAKE_UP_TIME_OUTED:
                            // bytes received while in power saving mode should be ignored
                            // MUST go through power up process before accepting any frame
                            // => wait again for GSM 07.10 frame flag
                            pPD->State = WAIT_FRAME;
                            break;

                        case WAKING_UP_TE_INITIATIVE :
                            SENDLOG((TEXT("710MUX: CDispatcher::ParseRecBytes: -> TE sent 1st frame\r\n")));

                            // let the wake up thread knows that TE is operational
                            PulseEvent(m_hWokenUpEv);

                            // let time to the wake up thread to react (if any active) 
                            WaitForSingleObject(m_hGoAheadEv, WAKE_UP_SEND_FLAG_INTERVAL);

                            // TE has sent its 1st frame => unblock send operation
                            LeaveCriticalSection(&m_csSend);

                            m_lPowerState = OPERATIONAL;
                            break;

                        case WAKING_UP_MS_INITIATIVE :
                            m_lPowerState = OPERATIONAL;
                            break;

                        default:                            
                            break;
                    }

                    if (OPERATIONAL == m_lPowerState)
                    {
                        pPD->State = READ_FRAME_HEADER;
                        pPD->Param.cbLength = 0;                    
                        pPD->cbData = 0;
                        continue;
                    }
                }
            }
            break;

            case READ_FRAME_HEADER:
            case READ_FRAME_LENGTH:
            case READ_FRAME_DATA:            
            {
                DWORD cbRead = 0;

                if (OPERATIONAL == m_lPowerState)
                {
                    cbRead = (this->*pPD->pfnReadFrame)(pPD, pRxBuffer, cbRxBuffer);
                }
                else
                {
                    pPD->State = READ_FRAME_ERROR;
                }
                
                switch (pPD->State)
                {
                    case READ_FRAME_COMPLETE:
                        {
                            //At this point we are guaranteed to have
                            //at least a header (2 bytes) and FCS (1 byte).

                            CChanel* pCh = m_ppChanel[GET_DLCI(pPD->pFrameBuf[0])];

                            if (pCh != NULL)
                            {
                                //Signal data to the channel with FCS byte excluded.
                                pCh->SignalEndOfFrame(pPD->pFrameBuf[1],
                                                      pPD->pFrameBuf + FRAME_HEADER_SIZE,
                                                      pPD->cbData - FRAME_SIZE_MIN);

                            }
                            else
                            {
                                SENDERR((TEXT("710MUX: CDispatcher::ParseRecBytes: bad DLCI (%u)\r\n"),
                                        GET_DLCI(pPD->pFrameBuf[0])));
                            }
                            pPD->bEndOfFrame = TRUE;
                            pPD->State = WAIT_FRAME;
                        }
                        break;

                    case READ_FRAME_ERROR:
                        pPD->State = WAIT_FRAME;
                        break;

                    default:
                        break;                    
                }
                
                pRxBuffer += cbRead;
                cbRxBuffer -= cbRead;
                continue;
            }
        }

        pRxBuffer++;
        cbRxBuffer--;
    }
}

//---------------------------------------------
// Reset radio
//---------------------------------------------
void CDispatcher::RequestResetIn(DWORD dwTimeOutBeforeReset)
{
    HANDLE hResetRadioTh;

    // save time-out value
    m_dwTimeOutBeforeReset = dwTimeOutBeforeReset;

    hResetRadioTh = CreateThread(NULL, 0, ResetRadioTh, this, 0, NULL);
    if ( NULL == hResetRadioTh )
    {
        // low memory issue => exit
        SENDERR((TEXT("710MUX: Dispatcher: -> ERROR CANNOT CREATE ResetRadioTh\r\n")));        
    }
    else
    {
        CloseHandle(hResetRadioTh);
    }
}

void CDispatcher::CancelResetRequest()
{
    // cancel reset request
    if (NULL != m_hCancelResetEv)
    {
        SetEvent(m_hCancelResetEv);
    }
}

//---------------------------------------------
// channel activity monitoring
//---------------------------------------------
void CDispatcher::BroadcastChnlIdleStatus()
{
    // broadcast idle status to all user registered channel
    // that are monitoring the channel activity
    for (long i = 1; i < MAX_CHANEL_ID -1; i++)
    {
        if ( NULL != m_ppChanel[i] )
        {
            ((CUserChanel *)m_ppChanel[i])->NotifyIdle();
        }
    }
}

//---------------------------------------------
// friend routines
//---------------------------------------------
DWORD WINAPI SenseComTh( PVOID lpParameter )
{
    CDispatcher* pDisp = (CDispatcher*)lpParameter;
    DWORD dwMask;
    DWORD dwErrors;
    bool bNeedTimeOut = false;
    BYTE* pBuf = NULL;
    DWORD cbRxBuffer = 0; 
    BYTE* pRxBuffer = NULL;   
    DWORD cbRecvd = 0;
    CDispatcher::tParseData ParseParam;

    memset (&ParseParam, 0, sizeof(CDispatcher::tParseData));

    __try 
    {
        SetThreadPriority(pDisp->m_hSenseComTh, THREAD_PRIORITY_ABOVE_NORMAL);

        // init the receive status

        cbRxBuffer = 2 * (pDisp->m_cbMaxFrameData + FRAME_SIZE_MIN);
        ParseParam.cbFrameBuf = pDisp->m_cbMaxFrameData + FRAME_SIZE_MIN;

        pBuf = (BYTE *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbRxBuffer + ParseParam.cbFrameBuf);
        
        if (NULL == pBuf)
        {
            SENDERR((TEXT("710MUX: SenseComTh: out of memory\r\n")));
            goto Exit;
        }

        pRxBuffer = pBuf;
        ParseParam.pFrameBuf = (pBuf + cbRxBuffer);
        
        ParseParam.State = CDispatcher::WAIT_FRAME;

        if (GSM0710_ADVANCED_FLAG == pDisp->m_bFrameFlag)
        {
            ParseParam.pfnReadFrame = CDispatcher::ReadAdvancedModeFrame;
        }
        else
        {
            ParseParam.pfnReadFrame = CDispatcher::ReadBasicModeFrame;
        }
        
#ifndef GSM0710_UNITARY_TESTS
        // set comm mask and wait for comm event
        SetCommMask(pDisp->m_hComPort, DEFAULT_COM_MASK);
#endif    // GSM0710_UNITARY_TESTS
        
        // wait launch ev
        WaitForSingleObject(pDisp->m_hGoAheadEv, INFINITE);
        ResetEvent(pDisp->m_hGoAheadEv);

        while(!pDisp->m_bTerminate)
        {
#ifdef GSM0710_UNITARY_TESTS
            // unitary tests => faked serial line
            WaitForSingleObject(hUTest_FakeRxEv,INFINITE);
            if (pDisp->m_bTerminate)
            {
                break;
            }

            if (WAKE_UP_TIME_OUTED != pDisp->m_lPowerState)
            {
                dwMask = EV_RXCHAR;
            }
            else
            {
                dwMask = 0;
            }
#else
            if (CDispatcher::WAIT_FRAME != ParseParam.State)
            {
                // need to time-out the complete frame reception
                pDisp->m_dwTimeOut = COMPLETEFRAME_TIMEOUT;
                SetEvent(pDisp->m_hLaunchTimeOutEv);
            }

            dwMask = 0;

            if ( !WaitCommEvent(pDisp->m_hComPort, &dwMask, NULL) )
            {
                if (pDisp->m_bTerminate)
                {
                    break;
                }
                else
                {
                    SENDERR((TEXT("710MUX: SenseComTh: WaitCommEvent err %i\r\n"), GetLastError()));
                    if (CDispatcher::WAIT_FRAME != ParseParam.State)
                    {
                        // give up current frame and try to re-sync frame
                        ParseParam.State = CDispatcher::WAIT_FRAME;                        
                    }
                    // cancel timeout
                    PulseEvent(pDisp->m_hCancelTimeOutEv);                    
                }
            }            

#endif // GSM0710_UNITARY_TESTS

            // check mask
            if (dwMask & EV_ERR)
            {
                ClearCommError(pDisp->m_hComPort, &dwErrors, NULL);                
            }

            if (dwMask & EV_BREAK)
            {
                ClearCommBreak(pDisp->m_hComPort);                
            }

            if (dwMask & EV_RXCHAR)
            {
                // cancel timeout
                PulseEvent(pDisp->m_hCancelTimeOutEv);

                while (ReadFile(pDisp->m_hComPort, pRxBuffer, cbRxBuffer, &cbRecvd, NULL) && cbRecvd != 0)
                {
                    pDisp->ParseRecBytes(&ParseParam, pRxBuffer, cbRecvd);
                }
            }

            if (WAKE_UP_TIME_OUTED == pDisp->m_lPowerState)
            {
                // wake up procedure initiated by TE has time out
                // - unblock send operation  
                // - set power saving state
                // - now wait for another frame
                //
                // Rem : 
                //    - see ParseRecBytes for entering m_csSend
                //    - WakeUpTh has wake up this thread by setting 
                //      a new comm mask
                //
                SENDLOG((TEXT("710MUX: CDispatcher::ParseRecBytes: -> TE initiated wake up proc time-out\r\n")));

                pDisp->m_lPowerState = POWER_SAVING;
                ParseParam.State = CDispatcher::WAIT_FRAME;
                LeaveCriticalSection(&pDisp->m_csSend);
            }
        }
    } 
    __except( DispFilterException(GetExceptionCode(), TEXT("SenseComTh")) ) 
    {
    }

Exit:
    if (pBuf != NULL)
    {
        HeapFree(GetProcessHeap(), 0, pBuf);
        pBuf = NULL;
    }
    
    return 0;
}

DWORD WINAPI TimeOutTh( PVOID lpParameter )
{
    CDispatcher* pDisp = (CDispatcher*)lpParameter;

    __try 
    {
        SetThreadPriority(pDisp->m_hTimeOutTh, THREAD_PRIORITY_ABOVE_NORMAL);

        while(!pDisp->m_bTerminateAll)
        {
            WaitForSingleObject(pDisp->m_hLaunchTimeOutEv, INFINITE);
            if (pDisp->m_bTerminateAll)
            {
                // termination request => leave
                break;
            }

            if (WAIT_TIMEOUT == WaitForSingleObject(pDisp->m_hCancelTimeOutEv, pDisp->m_dwTimeOut))
            {
                // We timed out -- cancel WaitCommEvent() in progress
                SetCommMask(pDisp->m_hComPort, DEFAULT_COM_MASK);
            }
        }
    } 
    __except( DispFilterException(GetExceptionCode(), TEXT("TimeOutTh")) ) 
    {
    }

    return 0;
}

DWORD WINAPI WakeUpTh( PVOID lpParameter )
{
    CDispatcher* pDisp = (CDispatcher*)lpParameter;

    DWORD dwNbWakeUpAttempt = (GSM0710_T3 / WAKE_UP_SEND_FLAG_INTERVAL);
    DWORD dwLen;    
    LONG lPowerStateTmp;
    HANDLE hResetRadioTh;

    __try 
    {
        BYTE bWakeUpChars[] = { pDisp->m_bFrameFlag, pDisp->m_bFrameFlag};

        while(!pDisp->m_bTerminate)
        {
            WaitForSingleObject(pDisp->m_hLaunchWakeUpEv, INFINITE);
            if (pDisp->m_bTerminate)
            {
                // termination request => leave
                break;
            }
                
            dwNbWakeUpAttempt = (GSM0710_T3 / WAKE_UP_SEND_FLAG_INTERVAL);
            InterlockedExchange(&lPowerStateTmp, pDisp->m_lPowerState);

            while(!pDisp->m_bTerminate && 
                  (POWER_SAVING == lPowerStateTmp || 
                  WAKING_UP_TE_INITIATIVE == lPowerStateTmp)
                  )
            {
                // send wake up byte
                WriteFile(pDisp->m_hComPort, bWakeUpChars, sizeof(bWakeUpChars), &dwLen, NULL);

                // wait for answer from TE 
                InterlockedExchange(&lPowerStateTmp, pDisp->m_lPowerState);
                if ((POWER_SAVING != lPowerStateTmp && WAKING_UP_TE_INITIATIVE != lPowerStateTmp) ||
                    WAIT_OBJECT_0 == WaitForSingleObject(pDisp->m_hWokenUpEv , WAKE_UP_SEND_FLAG_INTERVAL) )
                {
                    // TE in waking up process => can send frame, go ahead
                    SetEvent(pDisp->m_hGoAheadEv);
                    break;
                }

                dwNbWakeUpAttempt--;

                if (0 == dwNbWakeUpAttempt)
                {
                    SENDLOG((TEXT("710MUX: PowerUpTh: -> cannot wake up TE\r\n")));

                    // Reset the radio immediatly
                    pDisp->m_dwTimeOutBeforeReset = 0;
                    hResetRadioTh = CreateThread(NULL,0, ResetRadioTh, (LPVOID)pDisp, 0,NULL);
                    if ( NULL == hResetRadioTh )
                    {
                        // cannot create the reset thread
                        // => signal wake-up time out to upper layer
                        InterlockedExchange(&lPowerStateTmp, pDisp->m_lPowerState);
                        if (WAKING_UP_TE_INITIATIVE == lPowerStateTmp)
                        {
                            // TE initiated wake up procedure
                            // can't wake up => should wake the Rx thread up, to let it release the Send locker
                            InterlockedExchange(&pDisp->m_lPowerState, WAKE_UP_TIME_OUTED);
#ifndef GSM0710_UNITARY_TESTS
                            SetCommMask(pDisp->m_hComPort, DEFAULT_COM_MASK);
#else 
                            SetEvent(hUTest_FakeRxEv);
#endif    // GSM0710_UNITARY_TESTS
                        }
                        else
                        {
                            // MS initiated wake up procedure

                            // can't wake up => 'wake up' Send method 
                            InterlockedExchange(&pDisp->m_lPowerState, WAKE_UP_TIME_OUTED);
                            SetEvent(pDisp->m_hGoAheadEv);
                        }
                    }
                    else
                    {
                        // close handle
                        CloseHandle(hResetRadioTh);
                    }

                    break;
                }
            }
        }
    } 
    __except( DispFilterException(GetExceptionCode(), TEXT("WakeUpTh")) ) 
    {
    }

    return 0;
}

DWORD WINAPI ResetRadioTh( PVOID lpParameter )
{
    bool bRebootOK;
    CDispatcher* pDisp = (CDispatcher*)lpParameter;
    long i;

    __try 
    {
        // check if there is a time-out before reset
        if (0 != pDisp->m_dwTimeOutBeforeReset && NULL != pDisp->m_hCancelResetEv)
        {
            // wait for time out or cancel reset event
            if ( WAIT_TIMEOUT != WaitForSingleObject(pDisp->m_hCancelResetEv, pDisp->m_dwTimeOutBeforeReset) )
            {
                // reset request has been canceled
                goto End_thread;
            }
        }

        // assume that communication between radio module
        // and device is broken

        // set all connected channel to not connected state
        for (i = MAX_CHANEL_ID -1; i >=0; i--)
        {
            if ( NULL != pDisp->m_ppChanel[i] )
            {
                InterlockedDecrement(&pDisp->m_lConnectedChanelNb);
                InterlockedExchange(&(pDisp->m_ppChanel[i]->m_lState), DISCONNECTED);
            }
        }

        // terminate dispatcher
        pDisp->Terminate();

        // indicate that next init will required radio to be reseted 
        pDisp->m_bResetRequired = true;

        // restart everything
        bRebootOK = pDisp->ReinitAll();
        if (bRebootOK && NULL != pDisp->m_hRebootOKEv)
        {
            // signal reboot OK
            SetEvent(pDisp->m_hRebootOKEv);
        }
        else if (!bRebootOK && NULL != pDisp->m_hRebootKOEv)
        {
            // signal reboot KO
            SetEvent(pDisp->m_hRebootKOEv);
        }

        // reset is done (either if it has failed or not)
        pDisp->m_bResetRequired = false;

End_thread:
        pDisp->m_dwTimeOutBeforeReset = 0;

    } 
    __except( DispFilterException(GetExceptionCode(), TEXT("ResetRadioTh")) ) 
    {
    }

    return 0;
}
